use std::marker::PhantomData;

use sea_orm::query;
use sqlx::{
    postgres::{PgArguments, PgConnectOptions},
    Column, Connection, Database, Encode, PgConnection, Postgres, Row, Type, Value,
};

struct Condition<'q, DB, T>
where
    T: 'q + Send + Encode<'q, DB> + Type<DB>,
    DB: Database,
{
    conds: Vec<(&'q str, T)>,
    phantom_db: PhantomData<DB>,
    phantom_lifetime: PhantomData<&'q ()>,
}

impl<'q, DB, T> Condition<'q, DB, T>
where
    T: 'q + Send + Encode<'q, DB> + Type<DB>,
    DB: Database,
{
    pub fn new() -> Self {
        Self {
            conds: vec![],
            phantom_db: PhantomData,
            phantom_lifetime: PhantomData,
        }
    }

    pub fn add_condtion(mut self, cond: bool, where_sql: &'q str, value: T) -> Self {
        if !cond {
            return self;
        }
        self.conds.push((where_sql, value));

        self
    }

    pub fn get_conditon_list(self) -> Vec<(&'q str, T)> {
        self.conds
    }
}

#[test]
fn condition_test() {
    let mut cond = Condition::<Postgres, _>::new();
    cond = cond.add_condtion(true, "and username = ?", "jaakan");
    cond = cond.add_condtion(true, "and password = ?", "34");
    let cond = cond.get_conditon_list();
}

#[tokio::test]
async fn test_sqlx_0() {
    let _ = dotenvy::dotenv().unwrap();
    let db_url = std::env::var("DATABASE_URL").unwrap();
    println!("db url: {db_url}");

    let mut conn = PgConnection::connect(&db_url).await.unwrap();

    let rows = sqlx::query("select * from sys_user where username = $1 or username = $2")
        .bind("jaakan")
        .bind("admin")
        .fetch_all(&mut conn)
        .await
        .unwrap();

    for row in rows {
        let columns = row.columns();
        for (i, column) in columns.iter().enumerate() {
            println!("column: {i} {:?}", column.name());
        }
    }
}

enum Arg<'a> {
    Number(i32),
    Str(&'a str),
}

#[tokio::test]
async fn test_sqlx() {
    let _ = dotenvy::dotenv().unwrap();
    let db_url = std::env::var("DATABASE_URL").unwrap();
    println!("db url: {db_url}");

    let mut conn = PgConnection::connect(&db_url).await.unwrap();

    let mut cond_list = vec![];
    cond_list.push((r"and username=$", Arg::Str("jaakan")));
    cond_list.push((r"and id>$", Arg::Number(0)));

    let mut sql = vec!["select * from sys_user where 1=1".to_string()];
    for (i, (s, _)) in cond_list.iter().enumerate() {
        let n = i as u32 + 1_u32;
        let ss = format!("{}{}", s, n);
        sql.push(ss);
    }

    let sql = sql.join(" ");
    println!("sql:{}", sql);

    let mut query = sqlx::query::<Postgres>(sql.as_str());
    for (_, p) in cond_list {
        match p {
            Arg::Number(n) => query = query.bind(n),
            Arg::Str(n) => query = query.bind(n),
        }
    }

    let rows = query.fetch_all(&mut conn).await.unwrap();

    for row in rows {
        let columns = row.columns();
        for (i, column) in columns.iter().enumerate() {
            println!("column: {i} {:?}", column.name());
        }
    }
}
